package com.bittech.servlet.smallfileserver.dao.impl;

import com.bittech.servlet.smallfileserver.common.Page;
import com.bittech.servlet.smallfileserver.dao.SmallFileDao;
import com.bittech.servlet.smallfileserver.domain.SmallFile;
import com.bittech.servlet.smallfileserver.web.config.listener.MongoDBClientLoaderListener;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.GridFSUploadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.mongodb.client.gridfs.model.GridFSUploadOptions;
import org.apache.commons.codec.digest.DigestUtils;
import org.bson.BsonObjectId;
import org.bson.Document;
import org.bson.types.Binary;
import org.bson.types.ObjectId;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.function.Consumer;

/**
 * Author: secondriver
 * Created: 2018/8/27
 */
public class MongodbSmallDaoImpl implements SmallFileDao {
    
    private GridFSBucket gridFS = MongoDBClientLoaderListener.mongoDBClient.getGridFS();
    
    @Override
    public void save(SmallFile smallFile) {
        GridFSUploadOptions options = new GridFSUploadOptions()
                .metadata(
                        new Document()
                                .append("filename", smallFile.getName())
                                .append("contentType", smallFile.getContentType())
                                .append("md5", DigestUtils.md5Hex(smallFile.getContent().getData()))
                );
        GridFSUploadStream stream = gridFS.openUploadStream(
                new BsonObjectId(new ObjectId(smallFile.getId())),
                smallFile.getName(),
                options);
        stream.write(smallFile.getContent().getData());
        stream.close();
    }
    
    @Override
    public void delete(String id) {
        gridFS.delete(new ObjectId(id));
    }
    
    @Override
    public SmallFile find(String id) {
        GridFSDownloadStream stream = gridFS.openDownloadStream(new ObjectId(id));
        GridFSFile fsFile = stream.getGridFSFile();
        int fileLength = (int) fsFile.getLength();
        byte[] bytesToWriteTo = new byte[fileLength];
        stream.read(bytesToWriteTo);
        Binary binary = new Binary(bytesToWriteTo);
        SmallFile smallFile = convert(fsFile);
        smallFile.setContent(binary);
        return smallFile;
    }
    
    @Override
    public List<SmallFile> query(Page page) {
        List<SmallFile> smallFiles = new ArrayList<>();
        gridFS.find()
                .skip(page.getSkip())
                .limit(page.getPageSize())
                .sort(new Document("uploadDate", 1))
                .forEach((Consumer<GridFSFile>) gridFSFile -> smallFiles.add(convert(gridFSFile)));
        return smallFiles;
    }
    
    private SmallFile convert(GridFSFile fsFile) {
        SmallFile smallFile = new SmallFile();
        smallFile.setId(fsFile.getObjectId().toHexString());
        smallFile.setName(fsFile.getFilename());
        smallFile.setSize(fsFile.getLength());
        Calendar calendar = new Calendar.Builder().setInstant(fsFile.getUploadDate()).build();
        smallFile.setUploadDate(LocalDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault()));
        Document metadata = fsFile.getMetadata();
        if (metadata != null) {
            
            smallFile.setContentType(metadata.getString("contentType"));
            smallFile.setMd5(metadata.getString("md5"));
        }
        return smallFile;
    }
}
